#pragma once

#include <assert.h>
#include "ListElement.h"

//////////////////////////////////////////////////
//LinkedList
//////////////////////////////////////////////////
template <class Type> class LinkedList
  {

  public:
	  LinkedList();
	  ~LinkedList(void);

  private:
    unsigned long m_number_of_elements;
    ListElement<Type>* m_first_element;
    ListElement<Type>* m_last_element;

  public:
    void AddData(Type* i_data);
    void RemoveData(Type* i_data);

    Type* GetFirstElement() const;
    Type* GetLastElement() const;
    Type* GetNextElement(Type* i_data) const;

    int GetSize() const;
  };

//////////////////////////////////////////////////

//////////////////////////////////////////////////
//LinkedList
//////////////////////////////////////////////////

//------------------------------------------------

template <class Type> LinkedList<Type>::LinkedList() 
: m_number_of_elements(0)
, m_first_element(NULL)
, m_last_element(NULL)
  {
  }

//------------------------------------------------

template <class Type> LinkedList<Type>::~LinkedList() :
  {
  m_number_of_elements = 0;
  }

//------------------------------------------------

template <class Type> void LinkedList<Type>::AddData(Type* i_data)
  {
  if(i_data == NULL)
    {
    return;
    }

  if(m_first_element == NULL)
    {
    m_first_element = new ListElement<Type>(i_data);
    m_last_element = m_first_element;
    }
  else
    {
    ListElement<Type>* new_list_element = new ListElement<Type>(i_data);
    new_list_element->SetPrevListElement(m_last_element);
    m_last_element->SetNextListElement(new_list_element);
    m_last_element = new_list_element;
    }

  m_number_of_elements++;
  }

//------------------------------------------------

template <class Type> void LinkedList<Type>::RemoveData(Type* i_data)
  {
  ListElement<Type>* temp_list_element = m_first_element;
  while(temp_list_element != NULL)
    {
    if(temp_list_element->GetData() == i_data)
      {
      if(temp_list_element != m_first_element && temp_list_element != m_last_element)
        {
        temp_list_element->GetPrevListElement()->SetNextListElement(temp_list_element->GetNextListElement());
        temp_list_element->GetNextListElement()->SetPrevListElement(temp_list_element->GetPrevListElement());        
        }
      else if(temp_list_element == m_first_element && temp_list_element != m_last_element)
        {
        m_first_element = temp_list_element->GetNextListElement();
        m_first_element->SetPrevListElement(NULL);        
        }
      else if(temp_list_element == m_last_element && temp_list_element != m_first_element)
        {
        m_last_element = temp_list_element->GetPrevListElement();
        m_last_element->SetNextListElement(NULL);      
        }      
      else if(temp_list_element == m_last_element && temp_list_element == m_first_element)
        {
        m_last_element = NULL;
        m_first_element = NULL;
        }
      else
        {
        assert(0);
        }
      delete temp_list_element;      
      temp_list_element = NULL;
      m_number_of_elements--;
      continue;
      }         
    temp_list_element = temp_list_element->GetNextListElement();
    }
  }

//------------------------------------------------

template <class Type> Type* LinkedList<Type>::GetFirstElement() const
  {
  if(m_first_element)
    {
    return m_first_element->GetData();
    }
  else
    {
    return NULL;
    }
  }    

//------------------------------------------------

template <class Type> Type* LinkedList<Type>::GetLastElement() const
  {
  if(m_last_element)
    {
    return m_last_element->GetData();
    }
  else
    {
    return NULL;
    }
  }

//------------------------------------------------

template <class Type> Type* LinkedList<Type>::GetNextElement(Type* i_data) const
  {
  if(i_data)
    {
    ListElement<Type>* temp_list_element = m_first_element;
    while(temp_list_element != NULL && temp_list_element != m_last_element)
      {
      if(temp_list_element->GetData() == i_data)
        {
        return temp_list_element->GetNextListElement()->GetData();
        }    
      temp_list_element = temp_list_element->GetNextListElement();
      }
    }
  return NULL;
  }

//------------------------------------------------

template <class Type> int LinkedList<Type>::GetSize() const
  {
  return m_number_of_elements;
  }

//------------------------------------------------

//////////////////////////////////////////////////